
import Pycluster
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import mlab

def calcualte_transition_matrix(sessions, no_of_domains, bayes_estimation=True, prior_v=1.0, prior_e = 0.0):

    """
    esimation transition matrix from session
    bayes estimation uses prior information and makes all transition possible(with small probability)
    """
    model = np.zeros((no_of_domains, no_of_domains), np.float)

    prev = None
    # every domain in the session
    for session in sessions:
        for domain_string in session:
            domain = int(domain_string)
            if prev == None:
                prev = domain
                continue
            model[prev][domain] += 1
            prev = domain

    print 'normalizing'

    #normalize
    x = 0
    for x in range(no_of_domains):
        sumx = np.sum(model[x])
        if abs(sumx - 0) < 0.00001:
            continue
        for y in range(len(model)):
            if bayes_estimation is False:
                #well, kind a maximum likelihood
                model[x][y] /= sumx
            else:
                #some prior information, no transition with zero probability
                prior_e = 1.0 * len([c1 for c1 in model[x] if c1 > 0.00001])
                model[x][y] = (model[x][y] + prior_e) / ( sumx + prior_v)

    return model

# convert all the sessions to a markov model matrix
def convert_sessions_to_markov_models(file_path, no_of_domains):

    print 'start converting to markov models'

    session_count = 0
    sessions = []

    for line in open(file_path).read().splitlines():

        session_count += 1
        print session_count
        session = line.split(',')
        print 'session'
        print session
        sessions.append(session)

    print 'sessions'
    print sessions

    model = calcualte_transition_matrix(sessions, no_of_domains)
    print 'modeling finished'

    return model

def compute_distances(sessions, distance_fn):

    """
    compute distance matrix between sessions
    """

    nlen = len(sessions)
    distances = np.zeros((nlen, nlen), numpy.float)

    for i in range(nlen):
        for j in range(0, i):
            distances[i][j] = distances[j][i] = distance_fn(sessions[i], sessions[j])
    return distances

    """
    kcluster(data, nclusters=2, mask=None, weight=None,
             transpose=0, npass=1, method='a', dist='e',
             initialid=None) -> clusterid, error, nfound

    This function implements k-means clustering.
    data     : nrows x ncolumns array containing the expression data
    nclusters: number of clusters (the 'k' in k-means)
    method   : specifies how the center of a cluster is found:
               method=='a': arithmetic mean
               method=='m': median
    dist     : specifies the distance function to be used:
               dist=='e': Euclidean distance
               dist=='b': City Block distance
               dist=='c': Pearson correlation
               dist=='a': absolute value of the correlation
               dist=='u': uncentered correlation
               dist=='x': absolute uncentered correlation
               dist=='s': Spearman's rank correlation
               dist=='k': Kendall's tau

    Return values:
    clusterid: array containing the number of the cluster to which each
               gene/microarray was assigned in the best k-means clustering
               solution that was found in the npass runs;
    error:     the within-cluster sum of distances for the returned k-means
               clustering solution;
    nfound:    the number of times this solution was found.
    """
def kcluster(file_path, no_of_domains, no_of_clusters, clustering_method, distance_function):

    models = convert_sessions_to_markov_models(file_path, no_of_domains)
    print 'models'
    print models
    print 'length of model'
    print len(models)

    clusterids, error, nfound = Pycluster.kcluster(data=models, nclusters=no_of_clusters, method=clustering_method, dist=distance_function)

    print '###################'
    print 'clusterids'
    print clusterids
    print 'error'
    print error
    print 'nfound'
    print nfound

    return clusterids, error, nfound

def kmedoids(file_path, no_of_domains, no_of_clusters):

    """
    kmedoids clustering, requires distance matrix, therefore slow
    """
    distances = compute_distances(sessions, distance_fn)
    clusterids, error, nfound = Pycluster.kmedoids(distances, nclusters=clusters)
    return clusterids, error, nfound

    """
    This function implements a self-organizing map on a rectangular grid.
    data     : nrows x ncolumns array containing the gene expression data
    dist     : specifies the distance function to be used:
               dist=='e': Euclidean distance
               dist=='b': City Block distance
               dist=='c': Pearson correlation
               dist=='a': absolute value of the correlation
               dist=='u': uncentered correlation
               dist=='x': absolute uncentered correlation
               dist=='s': Spearman's rank correlation
               dist=='k': Kendall's tau

    Return values:
    clusterid: array with two columns, while the number of rows is equal to
               the number of genes or the number of microarrays depending on
               whether genes or microarrays are being clustered. Each row in
               the array contains the x and y coordinates of the cell in the
               rectangular SOM grid to which the gene or microarray was
               assigned.
    celldata:  an array with dimensions (nxgrid, nygrid, number of
               microarrays) if genes are being clustered, or (nxgrid,
               nygrid, number of genes) if microarrays are being clustered.
               Each element [ix][iy] of this array is a 1D vector containing
               the gene expression data for the centroid of the cluster in
               the SOM grid cell with coordinates (ix, iy).
    """
def somcluster(file_path, no_of_domains, distance_function):

    models = convert_sessions_to_markov_models(file_path, no_of_domains)
    clusterids, error, nfound = Pycluster.somcluster(data=models, dist=distance_function)
    return clusterids, error, nfound

    """
    This function implements the pairwise single, complete, centroid, and
    average linkage hierarchical clustering methods.
    data     : nrows x ncolumns array containing the gene expression data.
    dist     : specifies the distance function to be used:
               dist=='e': Euclidean distance
               dist=='b': City Block distance
               dist=='c': Pearson correlation
               dist=='a': absolute value of the correlation
               dist=='u': uncentered correlation
               dist=='x': absolute uncentered correlation
               dist=='s': Spearman's rank correlation
               dist=='k': Kendall's tau
    method   : specifies which linkage method is used:
               method=='s': Single pairwise linkage
               method=='m': Complete (maximum) pairwise linkage (default)
               method=='c': Centroid linkage
               method=='a': Average pairwise linkage
    """
def treecluster(file_path, no_of_domains, no_of_clusters, clustering_method, distance_function):

    models = convert_sessions_to_markov_models(file_path, no_of_domains)
    tree = Pycluster.treecluster(data=models, method=clustering_method, dist=distance_function)
    return tree.cut(no_of_clusters)